package client.dubbo;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.ServiceLoader;
import java.util.Set;

import org.apache.log4j.Logger;

import client.dubbo.IDubboClient.ConnectInfo;
import client.dubbo.impl.DO.DubboMethodTreeNode;
import client.dubbo.impl.DO.DubboRootTreeNode;
import client.dubbo.impl.DO.DubboServiceTreeNode;
import core.AlertUtil;
import core.TextAreaAppender;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class DubboClientController implements Initializable{

	/**
	 * Dubbo客户端名称与实例的Map集合
	 */
	private static final Map<String,IDubboClient> DUBBO_CLIENT=new HashMap<String,IDubboClient>();
	static {
		//通过服务发现查找Dubbo客户端实例
		ServiceLoader<IDubboClient> clients=ServiceLoader.load(IDubboClient.class);
		for(IDubboClient client:clients) {
			DUBBO_CLIENT.put(client.getName(), client);
		}
	}

	//应用日志对象
	private Logger appLogger;

	/**
	 * 根布局
	 */
	@FXML
	private BorderPane container;

	/**
	 * 左侧树结构
	 */
	@FXML
	private TreeView<DubboTreeNode> dubboTreeView;

	/**
	 * 信息组件
	 */
	@FXML
	private TextArea infoTextArea;

	/**
	 * 命令历史组件
	 */
	@FXML
	private TextArea cmdTextArea;

	/**
	 * 命令输入框
	 */
	@FXML
	private TextField cmdTextField;

	/**
	 * 日志组件
	 */
	@FXML
	private TextArea logTextArea;

	/**
	 * Dubbo客户端连接实例
	 */
	private IDubboClient dubboClient;

	@Override
	public void initialize(URL location, ResourceBundle resources) {
		appLogger = Logger.getLogger("dubbo_client_app");
		appLogger.addAppender(new TextAreaAppender(logTextArea));
		appLogger.info("日志输出初始化完成！");

		Platform.runLater(()->{
			Stage stage = (Stage)container.getScene().getWindow();
			 stage.setOnCloseRequest((event)->{
				 if(dubboClient!=null){
					 try {
						 dubboClient.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				 }
			 });
		});
	}

	/**
	 * 选择Dubbo客户端。
	 * 如果只有一个实例则直接使用，多个时弹出选择对话框进行选择。
	 * @return
	 */
	private IDubboClient choiceClient() {
		appLogger.info("进行客户端选择。");
		if(DUBBO_CLIENT.size() == 1) {
			appLogger.info("仅有一个客户端示例，无需选择！");
			//只有一种客户端
			for(IDubboClient client:DUBBO_CLIENT.values()) {
				return client;
			}
		}

		appLogger.info("启动客户端选择对话框。");
		//弹出选择对话框进行选择
		Set<String> clients = DUBBO_CLIENT.keySet();
		Optional<String> result = AlertUtil.choice("选择客户端", "请选择连接客户端类型：", clients);
		if(result.isPresent()) {
			appLogger.info("用户选择客户端："+result.get());
			return DUBBO_CLIENT.get(result.get());
		}

		return null;
	}

	/**
	 * 新建连接按钮监听
	 * @param e	事件对象
	 * @throws IOException　Dubbo连接操作可能发生异常
	 */
	public void createConnection(ActionEvent e) throws IOException{
		appLogger.info("检查是否存在已连接的客户端。");
		//如果Dubbo客户端实例不为空则先关闭之前的连接
		if(dubboClient != null) {
			appLogger.info("关闭已连接的客户端。");
			dubboClient.close();
		}

		//选择Dubbo客户端
		this.dubboClient = this.choiceClient();
		if(dubboClient != null) {
			//初始化Dubbo连接和组件
			if(dubboClient.init()){
				cmdTextArea.setText("已连接！\n\ndubbo>");
				initTreeView();
			}
		}
	}

	/**
	 * 初始化TreeView树
	 */
	private void initTreeView() {
		appLogger.info("初始化Dubbo树结构。");
		TreeItem<DubboTreeNode> root = this.getRootTreeItem();
		dubboTreeView.setRoot(root);

		appLogger.info("添加节点选择监听事件！");
		//节点选择事件处理
		dubboTreeView.getSelectionModel().selectedItemProperty().addListener((observable,oldValue,newValue)->{
			appLogger.info("Dubbo树节点被点击 -> oldValue:"+oldValue+",newValue:"+newValue);
			TreeItem<DubboTreeNode> currentSelectedItem = (TreeItem<DubboTreeNode>) newValue;
			if(currentSelectedItem==null) return;

			//设置节点信息
			DubboTreeNode selectedNode = currentSelectedItem.getValue();
			String itemInfo = this.getItemInfo(selectedNode);
			infoTextArea.setText(itemInfo);

			if(!selectedNode.isLeaf() && !selectedNode.isLoaded()) {
				currentSelectedItem.getChildren().clear();
				List<TreeItem<DubboTreeNode>> childNodeList = this.getChildren(selectedNode);
				currentSelectedItem.getChildren().addAll(childNodeList);
				currentSelectedItem.setExpanded(true);
			}

			selectedNode.setLoaded(true);
		});

		appLogger.info("设置树节点右键菜单！");
		//设置TreeView树邮件菜单
		MenuItem refreshItem = new MenuItem("刷新");
		refreshItem.setOnAction((event)->{
			appLogger.info("执行[刷新]动作！");
			TreeItem<DubboTreeNode> selectedItem = dubboTreeView.getSelectionModel().getSelectedItem();
			DubboTreeNode dubboTreeNode = selectedItem.getValue();
			//刷新信息
			infoTextArea.setText(getItemInfo(dubboTreeNode));

			//刷新子节点
			if(!dubboTreeNode.isLeaf()) {
				selectedItem.getChildren().clear();
				selectedItem.getChildren().addAll(this.getChildren(dubboTreeNode));
				selectedItem.setExpanded(true);
			}
		});

		MenuItem disconnectItem = new MenuItem("断开连接");
		disconnectItem.setOnAction((event)->{
			appLogger.info("执行[断开连接]动作！");
			try {
				dubboClient.close();
				dubboTreeView.setRoot(null);
				dubboTreeView.setContextMenu(null);
				infoTextArea.clear();
				cmdTextArea.setText("连接已断开！\n");
			} catch (IOException e) {
				AlertUtil.exception(e);
			}
		});

		MenuItem invokeItem = new MenuItem("调用方法");
		invokeItem.setOnAction((event)->{
			appLogger.info("执行[调用方法]动作！");
			TreeItem<DubboTreeNode> selectedItem = dubboTreeView.getSelectionModel().getSelectedItem();
			DubboTreeNode dubboTreeNode = selectedItem.getValue();
			if(dubboTreeNode instanceof DubboMethodTreeNode) {
				String service = ((DubboMethodTreeNode) dubboTreeNode).getService();
				String method = ((DubboMethodTreeNode) dubboTreeNode).getMethod();
				Optional<String> result = AlertUtil.input("调用方法","请输入方法参数：");
				if(result.isPresent()) {
					String params = result.get();
					String data = dubboClient.exeCmd("invoke "+service+"."+method+"("+params+")");
					AlertUtil.info("调用结果",data);
				}
			}
		});

		dubboTreeView.setContextMenu(new ContextMenu(disconnectItem));
		dubboTreeView.setOnContextMenuRequested((event)->{
			dubboTreeView.getContextMenu().getItems().clear();
			TreeItem<DubboTreeNode> selectedItem = dubboTreeView.getSelectionModel().getSelectedItem();
			DubboTreeNode DubboTreeNode = selectedItem.getValue();
			if(DubboTreeNode instanceof DubboRootTreeNode) {
				dubboTreeView.getContextMenu().getItems().add(disconnectItem);
			}else if(DubboTreeNode instanceof DubboMethodTreeNode) {
				dubboTreeView.getContextMenu().getItems().add(invokeItem);
			}
		});

		appLogger.info("Dubbo树结构初始化完成！");
	}

	/**
	 * 获取根节点
	 * @return　根节点对象
	 */
	private TreeItem<DubboTreeNode> getRootTreeItem() {
		appLogger.info("获取Dubbo树根节点！");
		ConnectInfo connectInfo = dubboClient.getConnectInfo();
		DubboRootTreeNode rootNode = new DubboRootTreeNode(connectInfo.getHost(),connectInfo.getPort());
		TreeItem<DubboTreeNode> rootItem = new TreeItem<DubboTreeNode>(rootNode);
		rootItem.setExpanded(true);
		rootItem.getChildren().addAll(this.getChildren(rootNode));
		return rootItem;
	}

	/**
	 * 获取子节点集合
	 * @param selectedNode　当前选中节点
	 * @return　子节点集合
	 */
	private List<TreeItem<DubboTreeNode>> getChildren(DubboTreeNode selectedNode) {
		appLogger.info("获取Dubbo树子节点！当前节点："+selectedNode);
		if(selectedNode instanceof DubboRootTreeNode) {
			List<TreeItem<DubboTreeNode>> childrenList = new ArrayList<TreeItem<DubboTreeNode>>();
			String result = dubboClient.exeCmd("ls");
			String[] lines = result.split("\n");
			for(int i=0;i<lines.length;i++) {
				if(lines[i]==null||"".equals(lines[i].trim())) {
					continue;
				}
				if(lines[i].startsWith("PROVIDER:")||lines[i].startsWith("dubbo>")) {
					continue;
				}
				String service = lines[i].trim();
				DubboServiceTreeNode serviceTreeNode = new DubboServiceTreeNode(service);
				TreeItem<DubboTreeNode> serviceItem = new TreeItem<DubboTreeNode>(serviceTreeNode);
				childrenList.add(serviceItem);
			}
			return childrenList;
		}else if(selectedNode instanceof DubboServiceTreeNode) {
			String service = ((DubboServiceTreeNode) selectedNode).getService();
			List<TreeItem<DubboTreeNode>> childrenList = new ArrayList<TreeItem<DubboTreeNode>>();
			String result = dubboClient.exeCmd("ls "+service);
			String[] lines = result.split("\n");
			for(int i=0;i<lines.length;i++) {
				if(lines[i]==null||"".equals(lines[i].trim())) {
					continue;
				}
				if(lines[i].startsWith(service)||lines[i].startsWith("dubbo>")) {
					continue;
				}
				String method = lines[i].trim();
				DubboMethodTreeNode methodTreeNode = new DubboMethodTreeNode(service,method);
				TreeItem<DubboTreeNode> methodItem = new TreeItem<DubboTreeNode>(methodTreeNode);
				childrenList.add(methodItem);
			}
			return childrenList;
		}
		return null;
	}

	/**
	 * 获取节点信息
	 * @param selectedNode　当前选中节点
	 * @return　节点信息
	 */
	private String getItemInfo(DubboTreeNode selectedNode) {
		appLogger.info("获取Dubbo树节点详情信息。当前节点："+selectedNode);
		if(selectedNode instanceof DubboRootTreeNode) {
			return removeCmdTip(dubboClient.exeCmd("status -l"));
		}else if(selectedNode instanceof DubboServiceTreeNode) {
			String service = ((DubboServiceTreeNode) selectedNode).getService();
			return removeCmdTip(dubboClient.exeCmd("count "+service));
		}else if(selectedNode instanceof DubboMethodTreeNode) {
			String service = ((DubboMethodTreeNode) selectedNode).getService();
			String method = ((DubboMethodTreeNode) selectedNode).getMethod();
			return removeCmdTip(dubboClient.exeCmd("count "+service+" "+method));
		}
		return null;
	}

	/**
	 * 删除命令提示部分
	 * @param str　待处理字符串
	 * @return　去除命令提示部分后的字符串
	 */
	private String removeCmdTip(String str) {
		if(str == null || "".equals(str)) {
			return "";
		}
		return str.replaceAll("dubbo>","").replaceAll("telnet>","");
	}

	/**
	 * 执行命令动作监听
	 * @param e　事件对象
	 */
	public void exeCmd(ActionEvent e) {
		if(dubboClient == null) {
			appLogger.error("无法执行用户输入命令！【未建立连接】");
			return;
		}

		String cmdStr = cmdTextField.getText();
		if(cmdStr == null || "".equals(cmdStr.trim())) {
			appLogger.error("无法执行用户输入命令！【命令为空】");
			return;
		}

		appLogger.info("执行用户输入命令："+cmdStr);
		//执行Dubbo命令
		String result = dubboClient.exeCmd(cmdStr);
		//清空命令输入框
		cmdTextField.clear();
		//将命令和响应追加到命令执行历史内
		cmdTextArea.appendText(cmdStr+"\n");
		cmdTextArea.appendText(result);
	}
}
